#include <stdlib.h>

#include "Instruction.hpp"
#include "Expression.hpp"

#include "Memory.hpp"

#include "ProgramManager.hpp"
#include "CallingContext.hpp"
#include "CallingInstance.hpp"

#include "InstructionFactory.hpp"
#include "ExpressionFactory.hpp"

extern FILE* yyin;

void
creerProgrammeMinimum ()
{
/*
    void main ()
    {
        float a, b, c;
        write ( "Saisir la valeur de a :" ); read ( a );
        write ( "Saisir la valeur de b :" ); read ( b );
        if ( a < b )
            c = a;
        else
            c = b;
        write ( "Le minimum de ", a, " et ", b, " est : ", c, "\n" );
    }
*/

    /*
        gestion de la table des identificateurs
    */
    unsigned int idmain = ProgramManager::addIdentificator ( "main" );
    unsigned int ida = ProgramManager::addIdentificator ( "a" );
    unsigned int idb = ProgramManager::addIdentificator ( "b" );
    unsigned int idc = ProgramManager::addIdentificator ( "c" );
    /*
        contexte lie a la fonction main
    */
    ProgramManager::createCallingContext ( idmain );
    /*
        gestion de la table des symboles de la fonction main
    */
    Type treal; treal._base = REAL; treal._modifier = NONE; treal._size = 0;
    ProgramManager::getCallingContext ( idmain )->addVariable ( ida, treal );
    ProgramManager::getCallingContext ( idmain )->addVariable ( idb, treal );
    ProgramManager::getCallingContext ( idmain )->addVariable ( idc, treal );
    /*
        gestion des instructions de la fonction main
    */
    std::vector < Expression* > lstexpr;
    std::vector < Instruction* > lstinstr;
    /* write ( "Saisir la valeur de a :" ); */
    lstexpr.push_back ( ExpressionFactory::generateConstant ( "Saisir la valeur de a :" ) );
    lstinstr.push_back ( InstructionFactory::generateOutput ( lstexpr ) );
    lstexpr.clear ();
    /* read ( a ); */
    lstexpr.push_back ( ExpressionFactory::generateVariable ( ProgramManager::getNameOf ( ida ) ) );
    lstinstr.push_back ( InstructionFactory::generateInput ( lstexpr ) );
    lstexpr.clear ();
    /* write ( "Saisir la valeur de b :" ); */
    lstexpr.push_back ( ExpressionFactory::generateConstant ( "Saisir la valeur de b :" ) );
    lstinstr.push_back ( InstructionFactory::generateOutput ( lstexpr ) );
    lstexpr.clear ();
    /* read ( b ); */
    lstexpr.push_back ( ExpressionFactory::generateVariable ( ProgramManager::getNameOf ( idb ) ) );
    lstinstr.push_back ( InstructionFactory::generateInput ( lstexpr ) );
    lstexpr.clear ();
    /* a < b */
    Expression* condition = ExpressionFactory::generateLt (
        ExpressionFactory::generateVariable ( ProgramManager::getNameOf ( ida ) ),
        ExpressionFactory::generateVariable ( ProgramManager::getNameOf ( idb ) ) );
    /* c = a; */
    Instruction* alors = InstructionFactory::generateAffectation (
        ExpressionFactory::generateVariable ( ProgramManager::getNameOf ( idc ) ),
        ExpressionFactory::generateVariable ( ProgramManager::getNameOf ( ida ) ) );
    /* c = b; */
    Instruction* sinon = InstructionFactory::generateAffectation (
        ExpressionFactory::generateVariable ( ProgramManager::getNameOf ( idc ) ),
        ExpressionFactory::generateVariable ( ProgramManager::getNameOf ( idb ) ) );
    /* if ( a < b ) c = a; else c = b; */
    lstinstr.push_back ( InstructionFactory::generateIfThenElse ( condition, alors, sinon ) );
    /* write ( "Le minimum de ", a, " et ", b, " est : ", c, "\n" ); */
    lstexpr.push_back ( ExpressionFactory::generateConstant ( "Le minimum de " ) );
    lstexpr.push_back ( ExpressionFactory::generateVariable ( ProgramManager::getNameOf ( ida ) ) );
    lstexpr.push_back ( ExpressionFactory::generateConstant ( " et de " ) );
    lstexpr.push_back ( ExpressionFactory::generateVariable ( ProgramManager::getNameOf ( idb ) ) );
    lstexpr.push_back ( ExpressionFactory::generateConstant ( " est : " ) );
    lstexpr.push_back ( ExpressionFactory::generateVariable ( ProgramManager::getNameOf ( idc ) ) );
    lstexpr.push_back ( ExpressionFactory::generateConstant ( "\n" ) );
    lstinstr.push_back ( InstructionFactory::generateOutput ( lstexpr ) );
    lstexpr.clear ();
    ProgramManager::getCallingContext ( idmain )->setCode ( InstructionFactory::generateBloc ( lstinstr ) );
    lstinstr.clear ();
}

void
creerProgrammeHelloWorld ()
{
/*
    void main ()
    {
        write ( "Hello World !\n" );
    }
*/

    /*
        gestion de la table des identificateurs
    */
    unsigned int idmain = ProgramManager::addIdentificator ( "main" );
    unsigned int ida = ProgramManager::addIdentificator ( "a" );
    /*
        contexte lie a la fonction main
    */
    ProgramManager::createCallingContext ( idmain );
    Type treal; treal._base = REAL; treal._modifier = NONE; treal._size = 0;
    ProgramManager::getCallingContext ( idmain )->addVariable ( ida, treal );
    /*
        gestion des instructions de la fonction main
    */
    /* write ( "Hello World !\n" ); */
    std::vector < Instruction* > lstinstr;
    std::vector < Expression* > lstexpr;
    /*
        les deux appels suivants sont valides, dans ce cas
    */
    lstexpr.push_back ( ExpressionFactory::generateConstant ( "La valeur de a est " ) );
    lstexpr.push_back ( ExpressionFactory::generateVariable ( "a" ) );
    lstexpr.push_back ( ExpressionFactory::generateConstant ( "\n" ) );
    lstinstr.push_back ( InstructionFactory::generateOutput ( lstexpr ) );
    lstinstr.push_back ( InstructionFactory::generateAffectation (
                                ExpressionFactory::generateVariable ( "a" ),
                                ExpressionFactory::generateConstant ( 8.2f ) ) );
    lstinstr.push_back ( InstructionFactory::generateOutput ( lstexpr ) );
    ProgramManager::getCurrentContext ()->setCode ( InstructionFactory::generateBloc ( lstinstr ) );
//    ProgramManager::getCallingContext ( idmain )->setCode ( InstructionFactory::generateOutput ( lstexpr ) );
}

extern int yyparse ();

int
main ( int argc, char** argv )
{
#ifdef UNITTEST
	Memory::unitaryTest ();
#endif
    if ( argc != 2 )
    {
        std::cerr << "usage: " << argv [ 0 ] << " <filename>" << std::endl;
        exit ( EXIT_FAILURE );
    }
    yyin = fopen ( argv [ 1 ], "r" );
    if ( yyin == NULL )
    {
        std::cerr << "unable to open file " << argv [ 1 ] << std::endl;
        exit ( EXIT_FAILURE );
    }
    setLogFlags ( EEXEC_LOG|
		  IEXEC_LOG|
		  PARSER_LOG|
		  EFACT_LOG|
		  IFACT_LOG|
		  EEXEC_LOG|
		  IEXEC_LOG);
    yyparse ();
    fclose ( yyin );
    for (   std::map < unsigned int, CallingContext* >::const_iterator it = ProgramManager::begin () ;
            it != ProgramManager::end () ;
            ++ it )
    {
        std::cout << "----------" << std::endl;
        std::cout << "function " << ProgramManager::getNameOf ( it->first ) << std::endl;
        it->second->display ();
        std::cout << "----------" << std::endl;
    }
    CallingInstance *c = ProgramManager::getCallingContext ( ProgramManager::getIndexOf ( "main" ) )->getInstance ( NULL );
    c->execute ();
    return 0;
}
